package main

import (
	"sync"
	"fmt"
)

type UserAges struct{
	ages map[string]int
	sync.RWMutex //互斥体
}

func (ua *UserAges) Add(name string,age int)  {    //传递user_i,i
	ua.Lock() //写锁
	defer ua.Unlock()//解除写锁
	ua.ages[name]=age //往map里写数据[name]=user_i,[age]=i
}
func (ua *UserAges) Get(name string)int  { //传参为user_%d
	ua.RLock() //读锁
	defer ua.RUnlock() //解除读锁

	if age,ok:=ua.ages[name];ok{   //遍历map中的元素，ua.ages[name]是获取值age，如果存在就返回元素的值
		fmt.Println("age:",age)
		return age     //0-999
	}
	return -1//map中没元素就输出-1
}

func main()  {
	count:=1000 //创建一个int变量 ，值是1000

	gw:=sync.WaitGroup{} //所有goroutine都执行完才退出主函数
	gw.Add(count*2)  //设置goroutine的执行次数
	u:=UserAges{ages:map[string]int{}} //创建一个结构体对象，值为map,
	fmt.Println(u)            // map[] {0,0} 花括号中的是key和value
	add:= func(i int) { //创建一个匿名函数，传参i整型
		u.Add(fmt.Sprintf("user_%d",i),i)//调用UserAges结构体对象的Add方法往map中添加元素
		//fmt.Sprintf返回的是字符串，两个string,user_0 0 ->user_999 999
		gw.Done()//WaitGroup -1
	}
	for i:=0;i<count;i++{  //执行一个for循环执行1000次
		go add(i) //执行goroutine1000次，往map中添加元素
		//go add(i) //执行goroutine1000次，往map中添加元素
	}

	for i:=0;i<count;i++{//执行一个for循环1000次
		go func(i int) {        //执行一个匿名函数，传参i进来
			defer gw.Done()   //每执行一次for循环就WaitGroup -1
			num:=u.Get(fmt.Sprintf("user_%d",i)) //调用Get方法读取map中的值
			fmt.Println(num)

		}(i) //传递参数i
	}
	gw.Wait()//程序阻塞，等待WaitGroup的数量为0
	fmt.Println("Done")
}

/*
sync.Mutex虽然有写锁功能，但是在用并发读写map是不安全，因为map属于引用类型，读写都指向同一个地址，所以读写会存在竞争关系。
报错为fatal error: concurrent map read and map write
另外golang提供一种锁，针对map是安全的。sync.RWMutex，这个互斥锁有两种方法，RLock()和Runlock,可以用这两种方法将读和写分开就可以了

*/